//Copyright (c) 1995, 2008, Oracle and/or its affiliates. All rights reserved.

import java.awt.Dimension;

import java.io.IOException;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreeSelectionModel;

import com.google.gson.*;

/**
 * Create a tree that allows the user to select the data to view
 * @author jcoston
 */
public class Tree extends JPanel implements TreeSelectionListener{


	private static final long serialVersionUID = 1L;
	private JTree tree;
    private static boolean DEBUG = true;
    private JScrollPane scrollPane = null;  
    private ArrayList<DatabaseInfo> ReswellDatabases = new ArrayList<DatabaseInfo>();
    private JFrame frame;
    
    public ArrayList<String> fieldArray = new ArrayList<String>();
    
    
    /**
     * Constructor that creates the tree
     * @param frame
     * @param jsonSchema
     */
	public Tree(JFrame frame, String jsonSchema){
		this.frame = frame;
		//Populate ReswellDatabases ArrayList
		try {
			parse(jsonSchema);
		} catch (IOException e) {
			e.printStackTrace();
		}
		
        //Create the nodes.
        DefaultMutableTreeNode top =
            new DefaultMutableTreeNode("Reswell");
        createNodes(top);

        //Create a tree that allows one selection at a time
        tree = new JTree(top);
        tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);

        //Listen for when the selection changes
        tree.addTreeSelectionListener(this);
        
      //Create the scroll pane and add the tree to it.
      JScrollPane treeView = new JScrollPane(tree);
      Dimension minimumSize = new Dimension(100, 50);
      treeView.setMinimumSize(minimumSize);
      add(treeView); 
            
	}

/**
* Dynamically adds the nodes to the tree
* @param top
*/
private void createNodes(DefaultMutableTreeNode top) {
    for(DatabaseInfo db : ReswellDatabases){
    	DefaultMutableTreeNode database = new DefaultMutableTreeNode(db.toString());

    	for(ReswellTable t : db.getTables()){
    		DefaultMutableTreeNode table = new DefaultMutableTreeNode(t.getTname());
        	database.add(table);
    	
        	for(ReswellField f : t.getFields()){
        		DefaultMutableTreeNode field = new DefaultMutableTreeNode(f.getAlternate());
        		field.setUserObject(f);
        		table.add(field);
        	}
        	
    	}
    	
    	top.add(database);
    }
}

/**
 * Required by TreeSelectionListener interface.
 * Dictates what will happen when a leaf is selected
 */
@Override
public void valueChanged(TreeSelectionEvent arg0) {
	DefaultMutableTreeNode node = (DefaultMutableTreeNode)tree.getLastSelectedPathComponent();
	
	if (node == null) return;
	
	Object nodeInfo = node.getUserObject();
    if (node.isLeaf()) {
        //Display a popup box that lets user enter constraint
    	//String constraint = getConstraint(node);
    	boolean gotten = getConstraint(node);
    	if(gotten){
	    	//Check if the field should be shown
	    	int n = JOptionPane.showConfirmDialog(
	    		    frame,
	    		    "Would you like to display this field?",
	    		    "Display This Field?",
	    		    JOptionPane.YES_NO_OPTION); 
	    	if (n == JOptionPane.YES_OPTION) {
	             ((ReswellField)node.getUserObject()).setSelected(true);
	    	}else
	    		((ReswellField)node.getUserObject()).setSelected(false);
	    	}
    	}
}

/**
 * Parse the input JsonSchema and populate the global variable ReswellDatabases with DatabaseInfos
 * @param jsonSchema databases to be displayed
 * @throws IOException
 */
public void parse(String jsonSchema) throws IOException{	
	JsonParser jparse = new JsonParser();
	for(JsonElement elem : jparse.parse(jsonSchema).getAsJsonArray()) {
		String DName = elem.getAsJsonObject().get("name").getAsString();
		JsonArray tables = elem.getAsJsonObject().get("tables").getAsJsonArray();
		ReswellDatabases.add(new DatabaseInfo(DName, tables));
	}
}

/**
 * Creates the pop-up box for the user to enter the constraints
 * @return boolean indicating if a valid constraint was entered by the user
 */
public boolean getConstraint(DefaultMutableTreeNode node){ //get rid of return
	String constrain = ((ReswellField) (node.getUserObject())).getConstraint();
	String message = "Enter the constraint:\n";	
	String s = ((String)JOptionPane.showInputDialog(
            frame,
            message,
            "Add a constraint",
            JOptionPane.PLAIN_MESSAGE,
            null, 
            null, 
            constrain)).trim();

	//If a string was returned, say so.
	if ((s != null) && (s.length() > 0)) {
		//check input is valid
		boolean processed = processInput(s,node);
		if(!processed){
			JOptionPane.showMessageDialog(
                    frame,
                    "Sorry, \"" + s + "\" "
                    + "isn't a valid response.\n",
                    s, JOptionPane.ERROR_MESSAGE);
			return false;
		}
		return true;
	}

	//If you're here, the return value was null/empty.
	System.out.println("No contraint was entered.");
	((ReswellField)node.getUserObject()).setOp("");
	((ReswellField)node.getUserObject()).setRight("");
	return true;
}

/**
 * Parse the input of the user and make sure it has the form operator number
 * @param in String entered by user
 * @param node DefaultMutableTreeNode selected by the user
 * @return boolean indicating if the entered value was correct
 */
public boolean processInput(String in, DefaultMutableTreeNode node){
	Matcher m = Pattern.compile("(=|==|<=|>=|!=|<|>) *(.+)").matcher(in.trim());
	if (m.find()) {
		((ReswellField)node.getUserObject()).setOp(m.group(1));
		((ReswellField)node.getUserObject()).setRight(m.group(2));
		return true;
	}
	return false;
//	in.trim().matches("(=|==|<=|>=|!=|<|>) *(.+)");
//	//remove possible white space
//	in.trim();
//	//check if input starts with two characters
//	if (in.startsWith("<=") | in.startsWith(">=")){
//		((ReswellField)node.getUserObject()).setOp(in.substring(0, 2));
//		String num = in.substring(2);
//		//make sure the second value is a number
//		if(num.matches("-?\\d+(\\.\\d+)?")){
//			((ReswellField)node.getUserObject()).setRight(num);
//			return true;
//		}
//		else
//			//there was an error
//			return false;
//		
//	}
//	//check if the input starts with one character
//	if (in.startsWith("=") | in.startsWith(">") | in.startsWith("<")){
//		((ReswellField)node.getUserObject()).setOp(in.substring(0, 1));
//		String num = in.substring(1);
//		//make sure the second value is a number
//		if(num.matches("-?\\d+(\\.\\d+)?")){
//			((ReswellField)node.getUserObject()).setRight(num);
//			return true;
//		}
//		else
//			//there was an error
//			return false;
//	}
//	//there was an error
//	return false;	
}

/**
 * Walk the jTree and create a GSON object that will be sent to the back-end
 * @return GSON String to be sent to back-end
 */
public String getJSONFilters(){
	//get the root
	fieldArray.clear();
	DefaultMutableTreeNode root = (DefaultMutableTreeNode)tree.getModel().getRoot();
	JsonObject rootObj = new JsonObject();
	JsonArray dbArray = new JsonArray();
	rootObj.add("databases", dbArray);
	
	//Walk the tree
	for(int i =0; i < root.getChildCount(); i++) {
		DefaultMutableTreeNode db = (DefaultMutableTreeNode) root.getChildAt(i);
		JsonObject dbObj = new JsonObject();
		JsonArray tblArray = new JsonArray();
		
		dbObj.add("name", new JsonPrimitive(db.toString()));
		
		for(int j = 0; j < db.getChildCount(); j++) {
			DefaultMutableTreeNode tbl = (DefaultMutableTreeNode) db.getChildAt(j);
			JsonObject tblObj = new JsonObject();
			tblObj.add("name", new JsonPrimitive(tbl.toString()));
			JsonArray fldArray = new JsonArray();
			JsonArray filters = new JsonArray();
			
			for (int k = 0; k < tbl.getChildCount();k++) {
				DefaultMutableTreeNode field = (DefaultMutableTreeNode) tbl.getChildAt(k);
				ReswellField rf = (ReswellField)field.getUserObject();
				if (rf.isSelected()){
					fldArray.add(new JsonPrimitive(rf.getFieldname()));
					fieldArray.add(rf.getFieldname());
				}
				JsonObject fltrObj = new JsonObject();
				fltrObj.add("left", new JsonPrimitive(rf.getFieldname()));
				fltrObj.add("op", new JsonPrimitive(rf.getOp()));
				fltrObj.add("right", new JsonPrimitive(rf.getRight()));
				if (!rf.getOp().isEmpty()) {
					// even if not selected, if it has a constraint it should be shown anyway
					if (!rf.isSelected()) fldArray.add(new JsonPrimitive(rf.getFieldname()));
					filters.add(fltrObj);
				}
			}

			if (fldArray.size() > 0) {
				tblObj.add("fields", fldArray);
				tblObj.add("filters", filters);
				tblArray.add(tblObj);
			}
		}
		
		dbObj.add("tables", tblArray);
		
		if (tblArray.size() > 0)
			dbArray.add(dbObj);
	}
	//create and return a GSON string
	return new Gson().toJson(rootObj);
}

}//End Tree